/**
 * Copyright (C) 2025 pony working team
 * SPDX-License-Identifier: MulanPSL-2.0
 */

/* includes (local) ----------------------------------------------------------*/
#include <kal.h>
/* includes (standard library, system) ---------------------------------------*/
#include <unistd.h>   // usleep
#include <stdlib.h>
#include <pthread.h>   // mutex
#include <semaphore.h>
#include <mqueue.h>

#include <sys/time.h>
#include <time.h>

#include <errno.h>

/* includes (other library) --------------------------------------------------*/
/* includes (project) --------------------------------------------------------*/

/* defines -------------------------------------------------------------------*/
/* typedefs ------------------------------------------------------------------*/

typedef void *(*pthread_entry)(void *);

typedef struct vmqueue {
    mqd_t id;
    const char *name;
} k_mqueue_t;

/* macro ---------------------------------------------------------------------*/
/* variables (extern) --------------------------------------------------------*/
/* variables (local) ---------------------------------------------------------*/
/* variables (global) --------------------------------------------------------*/
/* inlines -------------------------------------------------------------------*/
/* functions -----------------------------------------------------------------*/

static void __set_timespec(struct timespec *ts, uint32_t timeout)
{
    clock_gettime(CLOCK_REALTIME, ts);
    if (timeout == 0)
        return;

    ts->tv_sec += (timeout / 1000);
    ts->tv_nsec += ((timeout % 1000) * 1000);
}

int k_task_create(k_task_t *task, const char *name, uint8_t prio, size_t stack_size,
                  int (*entry)(uintptr_t arg), uintptr_t arg)
{
    pthread_t pid;

    if (x_magic_verify(task)) {
        return -1;
    }
    if (pthread_create(&pid, NULL, (pthread_entry)entry, (void *)arg) != 0) {
        return -1;
    }
    pthread_detach(pid);
    x_magic_set(task, pid);
    return 0;
}

int k_task_delete(k_task_t *task)
{
    if (!x_magic_verify(task)) {
        return -1;
    }
    pthread_t pid = (pthread_t)x_magic_get(task);
    if (pthread_cancel(pid) != 0) {
        return -1;
    }
    x_magic_reset(task);
    return 0;
}

int k_task_exit(void)
{
    pthread_exit(NULL);
    return 0;
}

int k_task_resume(k_task_t *task)
{
    return -ENOSYS;
}

int k_task_suspend(k_task_t *task)
{
    return -ENOSYS;
}

void k_task_lock(void)
{
}

void k_task_unlock(void)
{
}

void k_task_msleep(uint32_t ms)
{
    usleep(ms * 1000);
}

int k_task_prio_set(k_task_t *task, int prio)
{
    return -ENOSYS;
}

int k_task_prio_get(k_task_t *task)
{
    return -ENOSYS;
}

void k_task_stat(void)
{
}

int k_task_stack_space(k_task_t *task)
{
    return -ENOSYS;
}

int k_mutex_init(k_mutex_t *mutex)
{
    if (x_magic_verify(mutex) != 0) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
    if (m == NULL) {
        return -1;
    }
    if (pthread_mutex_init(m, NULL) != 0) {
        free(m);
        return -1;
    }
    x_magic_set(mutex, m);
    return 0;
}

int k_mutex_destroy(k_mutex_t *mutex)
{
    if (!x_magic_verify(mutex)) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)x_magic_get(mutex);
    if (m == NULL) {
        return -1;
    }
    if (pthread_mutex_destroy(m) != 0) {
        // return -1;
    }
    free(m);
    x_magic_reset(mutex);
    return 0;
}

int k_mutex_timedlock(k_mutex_t *mutex, uint32_t timeout)
{
    if (!x_magic_verify(mutex)) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)x_magic_get(mutex);
    if (m == NULL) {
        return -1;
    }
    struct timespec ts;
    __set_timespec(&ts, timeout);
    if (pthread_mutex_timedlock(m, &ts) != 0) {
        return -1;
    }
    return 0;
}

int k_mutex_trylock(k_mutex_t *mutex)
{
    if (!x_magic_verify(mutex)) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)x_magic_get(mutex);
    if (m == NULL) {
        return -1;
    }
    if (pthread_mutex_trylock(m) != 0) {
        return -1;
    }
    return 0;
}

int k_mutex_lock(k_mutex_t *mutex)
{
    if (!x_magic_verify(mutex)) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)x_magic_get(mutex);
    if (m == NULL) {
        return -1;
    }
    if (pthread_mutex_lock(m) != 0) {
        return -1;
    }
    return 0;
}

int k_mutex_unlock(k_mutex_t *mutex)
{
    if (!x_magic_verify(mutex)) {
        return -1;
    }
    pthread_mutex_t *m = (pthread_mutex_t *)x_magic_get(mutex);
    if (m == NULL) {
        return -1;
    }
    if (pthread_mutex_unlock(m) != 0) {
        return -1;
    }
    usleep(1000);
    return 0;
}

int k_sem_init(k_sem_t *sem, int limit, int value)
{
    if (x_magic_verify(sem)) {
        return -1;
    }
    sem_t *s = (sem_t *)malloc(sizeof(sem_t));
    if (s == NULL) {
        return -1;
    }

    if (sem_init(s, 0, value) != 0) {
        free(s);
        return -1;
    }
    x_magic_set(sem, s);
    return 0;
}

int k_sem_destroy(k_sem_t *sem)
{
    if (!x_magic_verify(sem)) {
        return -1;
    }
    sem_t *s = (sem_t *)x_magic_get(sem);
    if (sem_destroy(s) != 0) {
        return -1;
    }
    free(s);
    x_magic_reset(sem);
    return 0;
}

int k_sem_timedwait(k_sem_t *sem, uint32_t timeout)
{
    if (!x_magic_verify(sem)) {
        return -1;
    }
    struct timespec ts;

    __set_timespec(&ts, timeout);

    return sem_timedwait((sem_t *)x_magic_get(sem), &ts);
}

int k_sem_trywait(k_sem_t *sem)
{
    if (!x_magic_verify(sem)) {
        return -1;
    }
    return sem_trywait((sem_t *)x_magic_get(sem));
}

int k_sem_wait(k_sem_t *sem)
{
    if (!x_magic_verify(sem)) {
        return -1;
    }
    return sem_wait((sem_t *)x_magic_get(sem));
}

int k_sem_post(k_sem_t *sem)
{
    if (!x_magic_verify(sem)) {
        return -1;
    }
    return sem_post((sem_t *)x_magic_get(sem));
}

int k_event_init(k_event_t *event)
{
    return -ENOSYS;
}

int k_event_destroy(k_event_t *event)
{
    return -ENOSYS;
}

int k_event_timedrecv(k_event_t *event, uint32_t wanted, uint32_t option, uint32_t timeout,
                      uint32_t *recved)
{
    return -ENOSYS;
}

int k_event_tryrecv(k_event_t *event, uint32_t wanted, uint32_t option, uint32_t *recved)
{
    return -ENOSYS;
}

int k_event_recv(k_event_t *event, uint32_t wanted, uint32_t option, uint32_t *recved)
{
    return -ENOSYS;
}

int k_event_send(k_event_t *event, uint32_t value)
{
    return -ENOSYS;
}

int k_event_clear(k_event_t *event, uint32_t value)
{
    return -ENOSYS;
}

int k_mq_create(k_mq_t *mq, const char *name, size_t msg_size, size_t max_msgs, uint32_t options)
{
    k_mqueue_t *q;
    struct mq_attr attr;

    if (name == NULL) {
        return -1;
    }
    if (x_magic_verify(mq)) {
        return -1;
    }

    q = (k_mqueue_t *)malloc(sizeof(k_mqueue_t));
    if (q == NULL) {
        return -1;
    }

    // attr.mq_maxmsg <= /proc/sys/fs/mqueue/msg_max (10)
    // attr.mq_msgsize <= /proc/sys/fs/mqueue/msgsize_max (8192)
    attr.mq_maxmsg = max_msgs;
    attr.mq_msgsize = msg_size;

    q->id = mq_open(name, O_RDWR | O_CREAT | O_EXCL, 0666, &attr);   // name format is "/xxxx"
    if (q->id < 0) {
        if (errno == EEXIST) {
            mq_unlink(name);
        }
        free(q);
        return -1;
    }
    q->name = name;   // name is global
    x_magic_set(mq, q);

    return 0;
}

int k_mq_delete(k_mq_t *mq)
{
    if (!x_magic_verify(mq)) {
        return -1;
    }
    k_mqueue_t *q = (k_mqueue_t *)x_magic_get(mq);
    if (q == NULL || q->name == NULL) {
        return -1;
    }
    if (mq_close(q->id) != 0) {
        return -1;
    }
    mq_unlink(q->name);
    free(q);
    x_magic_reset(mq);

    return 0;
}

int k_mq_timedsend(k_mq_t *mq, const void *buf, size_t len, uint32_t timeout)
{
    if (!x_magic_verify(mq)) {
        return -1;
    }
    struct timespec ts;
    k_mqueue_t *q = (k_mqueue_t *)x_magic_get(mq);

    if (q == NULL) {
        return -1;
    }
    __set_timespec(&ts, timeout);

    return mq_timedsend(q->id, buf, len, 1, &ts);
}

int k_mq_trysend(k_mq_t *mq, const void *buf, size_t len)
{
    return k_mq_timedsend(mq, buf, len, 0);
}

int k_mq_send(k_mq_t *mq, const void *buf, size_t len)
{
    if (!x_magic_verify(mq)) {
        return -1;
    }
    k_mqueue_t *q = (k_mqueue_t *)x_magic_get(mq);
    if (q == NULL) {
        return -1;
    }
    return mq_send(q->id, buf, len, 1);
}

int k_mq_timedrecv(k_mq_t *mq, void *buf, size_t len, uint32_t timeout)
{
    if (!x_magic_verify(mq)) {
        return -1;
    }
    struct timespec ts;
    k_mqueue_t *q = (k_mqueue_t *)x_magic_get(mq);

    if (q == NULL) {
        return -1;
    }
    __set_timespec(&ts, timeout);

    return mq_timedreceive(q->id, buf, len, NULL, &ts);
}

int k_mq_tryrecv(k_mq_t *mq, void *buf, size_t len)
{
    return k_mq_timedrecv(mq, buf, len, 0);
}

int k_mq_recv(k_mq_t *mq, void *buf, size_t len)
{
    if (!x_magic_verify(mq)) {
        return -1;
    }
    k_mqueue_t *q = (k_mqueue_t *)x_magic_get(mq);
    if (q == NULL) {
        return -1;
    }
    return mq_receive(q->id, buf, len, NULL);
}

int k_timer_create(k_timer_t *timer, uint32_t interval, uint16_t mode, void (*pfn)(uintptr_t arg),
                   uintptr_t arg)
{
    return -ENOSYS;
}

int k_timer_delete(k_timer_t *timer)
{
    return -ENOSYS;
}

int k_timer_start(k_timer_t *timer)
{
    return -ENOSYS;
}

int k_timer_stop(k_timer_t *timer)
{
    return -ENOSYS;
}

uint64_t k_mseconds(void)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);

    return (uint64_t)(tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

uint32_t k_tick2ms(uint32_t ticks)
{
    return ticks;
}

uint32_t k_ms2tick(uint32_t ms)
{
    return ms;
}
